「 Velero 」源码走读 — Provider
based on v1.6.3
Storage Provider
pkg/persistence/object_store.go
StorageProvider 提供了一系列的封装了 ObjectStore Plugin 的接口,用于操作位于 BackupStorageLocation 上数据。
IsValid
调用接口
- ListCommonPrefixes(<bucket>, <prefix>/, /)
主体逻辑
针对获取到的公共前缀子目录,获取其子目录层级是否为 backups、restores、restic、metadata 和 plugins 之一,如果不是则返回 error,认为 BackupStorageLocation 不可用。
例如,BackupStorageLocation 上的目录层级为 <bucket>/<prefix>/backups/xxx 和 <bucket>/<prefix>/invalid/xxx,调用 ListCommonPrefixes,获取到的公共前缀子目录层级有 prefix/backups 和 prefix/invalid,进一步处理后,获取到的子目录层级为 backups 和 invalid,其中,invalid 不满足 5 个固定的名称之一,因此认为 BackupStorageLocation 不可用。
应用场景
- BackupStorageLocation Controller 会通过该接口周期性检查 BackupStorageLocation 是否可用
ListBackups
调用接口
- ListCommonPrefixes(<bucket>, <prefix>/backups/, /)
主体逻辑
针对获取到的公共前缀子目录,其子目录层级为 Backup 的名称,聚合并返回。
例如,BackupStorageLocation 上的目录层级为 <bucket>/<prefix>/backups/backupA 和 <bucket>/<prefix>/backups/backupB,调用 ListCommonPrefixes,获取到的公共前缀子目录层级有 <prefix>/backups/BackupA 和 <prefix>/backups/BackupB,进一步处理后,获取到的子目录层级为 BackupA 和 BackupB,则返回包含两者的列表。
应用场景
- BackupSync Controller 在同步 Backup 时,用于获取 BackupStorageLocation 中的 Backup
PutBackup
调用接口
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>.logs.gz, <log>)
- PutObject(<bucket>, <prefix>/backups/<backup>/velero-backup.json, <metadata>)
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>.tar.gz, <content>)
- DeleteObject(<bucket>, <prefix>/backups/<backup>/velero-backup.json)
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>-podvolumebackups.json.gz, <podvolumebackups>)
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>-volumesnapshots.json.gz, <volumesnapshots>)
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>-resource-list.json.gz, <resource-list>)
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>-csi-volumesnapshots.json.gz, <csi-volumesnapshots>)
- PutObject(<bucket>, <prefix>/backups/<backup>/<backup>-csi-volumesnapshotcontents.json.gz, <csi-volumesnapshotcontents>)
主体逻辑
按照以下顺序上传文件
- Backup Logs
备份过程中,Velero 服务产生的日志 - Backup Metadata
描述 Backup 本身的信息 - Backup Content
被备份的资源内容 - PodVolumeBackups
被备份的 Pod 卷信息,由 Restic 创建并维护 - PodVolumeSnapshots
被备份的 Pod 卷快照信息,由 SnapshotProvider 创建并维护 - Backup ResourcesList
被备份的资源清单 - CSI VolumeSnapshot
被备份的 Pod 卷快照信息,由 CSI 创建并维护 - CSI VolumeSnapshotContents
被备份的 Pod 卷快照内容信息,由 CSI 创建并维护
第一步日志上传失败时,仅会打印错误日志,而不会中断上传流程,是因为该步骤不影响备份的主体逻辑。此后如果有步骤失败,会影响到 Backup 的状态,并会清空之前的操作,即调用 DeleteObject 删除已上传的数据。
应用场景
- Backup Controller 在备份完成时,备份的资源信息上传至 BackupStorageLocation 时
GetBackupMetadata
调用接口
- GetObject(<bucket>, <prefix>/backups/<backup>velero-backup.json)
主体逻辑
获取到 velero-backup.json 文件,读取内容,解析成 Backup 对象格式。
应用场景
- BackupSync Controller 在同步 Backup 时,用于构建待同步的 Backup 对象
GetBackupVolumeSnapshots
调用接口
- ObjectExists(<bucket>, <prefix>/<backups>/<backup>/<backup>-volumesnapshots.json.gz)
- GetObject(<bucket>, <prefix>/<backups>/<backup>/<backup>-volumesnapshots.json.gz)
主体逻辑
判断 BackupVolumeSnapshots 是否存在,如果存在,则获取并解析返回。
应用场景
- Restore Controller 在恢复时,会获取 Backup 相关联的 VolumeSnapshot 并恢复
- BackupDeletion Controller 在删除备份时,会一并删除 VolumeSnapshot 信息
GetBackupVolumeBackups
调用接口
- ObjectExists(<bucket>, <prefix>/<backups>/<backup>/<backup>-podvolumebackups.json.gz)
- GetObject(<bucket>, <prefix>/<backups>/<backup>/<backup>-podvolumebackups.json.gz)
主体逻辑
判断 PodVolumeBackup 是否存在,如果存在,则获取并解析返回。
应用场景
- BackupSync Controller 在处理需要同步的 Backup 时,也会判断是否有相关联的 PodVolumeBackup 需要被同步
GetBackupContents
调用接口
GetObject(<bucket>, <prefix>/backups/<backup>/<backup>.tar.gz)
主体逻辑
调用接口,判断指定的备份内容。
应用场景
- Restore Controller 在恢复时,会将备份的内容下载到临时文件中,恢复创建资源
- BackupDeletion Controller 在删除备份时,如果定义了 action,会将备份的内容下载到临时文件中,获取到 action 定义的动作并执行
GetCSIVolumeSnapshots
调用接口
- ObjectExists(<bucket>, <prefix>/<backups>/<backup>/<backup>-csi-volumesnapshots.json.gz)
- GetObject(<bucket>, <prefix>/<backups>/<backup>/<backup>-csi-volumesnapshots.json.gz)
主体逻辑
调用 ObjectExists 判断 VolumeSnapshots 是否存在,如果存在,则获取并解析返回
应用场景
- 无
GetCSIVolumeSnapshotContents
调用接口
- ObjectExists(<bucket>, <prefix>/<backups>/<backup>/<backup>-csi-volumesnapshotscontents.json.gz)
- GetObject(<bucket>, <prefix>/<backups>/<backup>/<backup>-csi-volumesnapshotscontents.json.gz)
主体逻辑
判断 VolumeSnapshotsContents 是否存在,如果存在,则获取并解析返回
应用场景
- BackupSync Controller 在获取到需要同步 Backup 时,如果 Velero 开启了 CSI 特性,用于获取 VolumeSnapshotContents 对象,后续也会同步该对象
BackupExists
调用接口
- ObjectExists(<bucket>, <prefix>/backups/<backup>/velero-backup.json)
主体逻辑
调用接口,判断给定的备份是否存在。
应用场景
- Backup Controller 在备份时,会判断本次创建的备份在 BackupStorageLocation 中是否已经存在,如果存在则将备份状态设为 Failed
DeleteBackup
调用接口
- ListObjects(<bucket>, <prefix>/backups/<backup>/)
- DeleteObject(<bucket>, <key>)
主体逻辑
调用 ListObjects,获取指定备份名称目录下的所有文件,即 key,遍历所有文件,调用 DeleteObject 删除 key。
可以看到,Velero 在删除备份时仅会调用 DeleteBackup 删除所有的子文件,也就是所有的 key,但是不会删除这个备份目录,因此如果先要实现删除最后的空目录,需要在 StorageProvider 的 DeleteObject 接口实现。
应用场景
- BackupDeletion Controller 删除指定备份时,用于删除 BackupStorageLocation 中的 Backup 数据
PutRestoreLog
调用接口
- PutObject(<bucket>, <prefix>/restores/<restore>/restore-<restore>-logs.gz, <log>)
主体逻辑
调用接口,上传恢复日志文件。
应用场景
- Restore Controller 在恢复完成时,用于上传恢复过程中 Velero 产生的日志
PutRestoreResults
调用接口
- PutObject(<bucket>, <prefix>/restores/<restore>/restore-<restore>-logs.gz, <result>)
主体逻辑
调用接口,上传恢复结果信息。
应用场景
- Restore Controller 在恢复完成时,用于将本次恢复过程中产生的 warning 和 error 信息上传
DeleteRestore
调用接口
- ListObject(<bucket>, <prefix>/restores/<restore>/)
- DeleteObject(<bucket>, <key>)
主体逻辑
调用 ListObjects,获取指定恢复名称目录下的所有文件,即 key,遍历所有文件,调用 DeleteObject 删除 key。
可以看到,Velero 在删除恢复时仅会调用 DeleteBackup 删除所有的子文件,也就是所有的 key,但是不会删除这个恢复目录,因此如果先要实现删除最后的空目录,需要在 StorageProvider 的 DeleteObject 接口实现。
应用场景
- BackupDeletion Controller 在删除 Backup 时,会同步删除相关联的 Restore,并删除 BackupStorageLocation 中的 Restore 信息
BackupStorageLocation 中 Restore 的删除仅在该场景下,手动删除 Restore,不会触发 DeleteRestore 流程,也就是通过 velero restore delete xxx 是无法删除 BackupStorageLocation 中 Restore 文件的。
GetDownloadURL
调用接口
- CreateSignedURL(<bucket>, <target>, 10min)
target 表示要根据 DownloadRequest 对象要获取的目标文件,具体参考 DownloadRequest 章节
主体逻辑
根据 DownloadRequest 的对象类别,即 target,构建不同的目标文件路径,调用接口,获取 DownloadURL。
应用场景
- DownloadRequest Controller 在处理 DownloadRequest 对象时,会通过该接口构建 DownloadURL,并回写至 DownloadRequest 对象中
Snapshot Provider
pkg/backup/item_backupper.go
pkg/restore/pv_restorer.go
Velero 并未像 Storage Provider 封装一些上层接口,而是将底层接口的调用简单封装了以下两个函数,用于备份和恢复时,对于 PV 类型的资源做的快照和恢复的操作。
takePVSnapshot
调用接口
- init(snapshotLocation.Spec.Config)
- GetVolumeID(<Unstructured PV>)
- GetVolumeInfo(<volumeID>, <volumeZone>)
- CreateSnapshot(<volumeID>, <volumeZone>, <tags>)
主体逻辑
- 判断 Backup 的 SnapshotVolumes 是否开启,如果开启则表示需要对卷做快照,继续执行以下逻辑
- 如果 PV 已经被认领,则需要判断是否被 Restic 已经备份(没有被认领的 PV 肯定不会被 Restic 备份),如果已经备份,则不会再次创建卷快照
- 通过 PV 的 label 获取 PV 的 zone 信息(topology.kubernetes.io/zone 或者 failure-domain.beta.kubernetes.io/zone)
- 针对 backup 中每一个 Snapshot Location,初始化一个 volumeSnapshotter,调用 GetVolumeID 尝试获取 VolumeID
PV 卷肯定是由 Snapshot Provider 创建出来的,所以 GetVolumeID 肯定会有记录保存 - 根据 Backup 的 label 创建 Tag,并追加 velero.io/backup=<backupName> 和 velero.io/pv=<pvName>
- 调用 GetVolumeInfo 接口,获取到卷信息,包括卷类型和 IOPS
- 根据以上信息构建 volumeSnapshot 对象,调用 CreateSnapshot 接口,创建快照
- 更新 volumeSnapshot 状态等信息
executePVAction
调用接口
- init(snapshotLocation.Spec.Config)
- CreateVolumeFromSnapshot(<snapshotID>, <volumeType>, <volumeZone>, <volumeIOPS>)
- SetVolumeID(<pv>, <VolumeID>)
主体逻辑
- 校验 PV 的合法性,判断名称是否存在
- 判断是否需要通过快照恢复卷,即 Backup 中是否指定了备份卷(backup.snapshotVolumes)以及 Restore 中是否指定了恢复卷(restore.restorePVs)
- 根据 PV 名称以及 Restore 相关信息获取 snapshot 对象
- 如果获取到 Snapshot 对象,则初始化 volumeSnapshotter
- 根据 snapshot 对象中的卷信息,如卷类型,zone,IOPS 等信息,调用 CreateVolumeFromSnapshot 创建卷,并获取 VolumeID 信息
- 调用 SetVolumeID,给 PV 设置 VolumeID,并返回 PV 的解构类型
「 Velero 」源码走读 — Provider
http://shenxianghong.github.io/2022/02/20/2022-02-20 Velero 源码走读 - Provider/